package org.btik.light.tool.type;


import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * 类型工具
 */
public class TypeUtil {

    /**
     * 值转化工具
     */
    private static final ConcurrentHashMap<Class<?>, Function<String, Object>> valueParserMap = new ConcurrentHashMap<>();


    /**
     * 基础类型
     */
    private static final HashSet<Class<?>> BASE_TYPE = new HashSet<>();

    /**
     * 基础和装箱的类型
     */
    private static final HashSet<Class<?>> BASE_AND_BOX_TYPE = new HashSet<>();

    /**
     * 基础类型和装箱类型和字符串
     */
    private static final HashSet<Class<?>> COMMON_TYPE = new HashSet<>();

    /**
     * char 解析器
     *
     * @param str 待解析的值
     */
    public static char ofCharValue(String str) {
        if (str == null) {
            throw new IllegalArgumentException("Can't convert null to char");
        }
        // 可能是值，而非字符
        if (str.length() > 1) {
            try {
                int charValue = Integer.parseInt(str);
                return (char) charValue;
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("Cannot convert " + str + " to  char");
            }
        }
        // 是字符

        if (str.length() == 1) {
            return str.charAt(0);
        }
        throw new IllegalArgumentException("Cannot convert " + str + " to  char");
    }

    static {
        valueParserMap.put(String.class, (str) -> str);
        valueParserMap.put(int.class, Integer::parseInt);
        valueParserMap.put(Integer.class, Integer::parseInt);
        valueParserMap.put(long.class, Long::parseLong);
        valueParserMap.put(Long.class, Long::parseLong);
        valueParserMap.put(boolean.class, Boolean::parseBoolean);
        valueParserMap.put(Boolean.class, Boolean::parseBoolean);
        valueParserMap.put(short.class, Short::parseShort);
        valueParserMap.put(Short.class, Short::parseShort);
        valueParserMap.put(float.class, Float::parseFloat);
        valueParserMap.put(Float.class, Float::parseFloat);
        valueParserMap.put(double.class, Double::parseDouble);
        valueParserMap.put(Double.class, Double::parseDouble);
        valueParserMap.put(char.class, TypeUtil::ofCharValue);
        valueParserMap.put(Character.class, TypeUtil::ofCharValue);
        valueParserMap.put(byte.class, Byte::parseByte);
        valueParserMap.put(Byte.TYPE, Byte::parseByte);

        BASE_TYPE.add(int.class);
        BASE_TYPE.add(long.class);
        BASE_TYPE.add(boolean.class);
        BASE_TYPE.add(short.class);
        BASE_TYPE.add(float.class);
        BASE_TYPE.add(double.class);
        BASE_TYPE.add(char.class);
        BASE_TYPE.add(byte.class);
        BASE_AND_BOX_TYPE.addAll(BASE_TYPE);
        BASE_AND_BOX_TYPE.add(Integer.class);
        BASE_AND_BOX_TYPE.add(Long.class);
        BASE_AND_BOX_TYPE.add(Boolean.class);
        BASE_AND_BOX_TYPE.add(Short.class);
        BASE_AND_BOX_TYPE.add(Float.class);
        BASE_AND_BOX_TYPE.add(Double.class);
        BASE_AND_BOX_TYPE.add(Character.class);
        BASE_AND_BOX_TYPE.add(Byte.class);

        COMMON_TYPE.addAll(BASE_AND_BOX_TYPE);
        COMMON_TYPE.add(String.class);
    }

    public static Object parseValueFromString(String value, Class<?> type) {
        // 非基础类型接收null值
        if (!isBaseType(type) && value == null) {
            return null;
        }
        Function<String, Object> function = valueParserMap.get(type);
        if (null == function) {
            throw new IllegalArgumentException("cannot convert value " + value + " to type " + type.getName());
        }
        return function.apply(value);
    }

    public static void registerResolver(Class<?> type, Function<String, Object> parser) {
        valueParserMap.put(type, parser);
    }

    public static boolean isCommonType(Class<?> type) {
        return COMMON_TYPE.contains(type);
    }

    public static boolean isBaseType(Class<?> type) {
        return BASE_TYPE.contains(type);
    }

    public static boolean isBaseOrBoxType(Class<?> type) {
        return BASE_AND_BOX_TYPE.contains(type);
    }

    public static boolean isBaseType(Object o) {
        return o != null && isBaseType(o.getClass());
    }

    public static boolean isCommonType(Object o) {
        return o != null && isBaseType(o.getClass());
    }

    public static boolean isBaseOrBoxType(Object o) {
        return o != null && isBaseOrBoxType(o.getClass());
    }


}
